package io.r2mo.test.jce;

import io.r2mo.jce.common.HED;
import io.r2mo.jce.component.lic.domain.LicenseConfiguration;
import io.r2mo.jce.component.lic.domain.LicenseData;
import io.r2mo.jce.component.lic.domain.LicenseFile;
import io.r2mo.jce.component.lic.domain.LicensePath;
import io.r2mo.jce.constant.LicFormat;
import io.r2mo.typed.common.Binary;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;

import javax.crypto.SecretKey;
import java.security.PrivateKey;

/**
 * @author lang : 2025-09-19
 */
@Slf4j
public class LicEncTestCase extends AbstractLicenseTestCase {

    /**
     * 测试：写入并加密新的 License 文件
     * <pre>
     * 数据流：
     *   1. 加载原始 JSON -> 转换成 LicenseData（业务实体数据）
     *   2. 动态追加机器指纹（signFinger）作为硬件绑定因子
     *   3. 构造 LicenseConfiguration（包含存储目录、公私钥路径）
     *   4. 加载私钥（用于数字签名）
     *   5. 生成随机对称密钥 SecretKey（用于对 LicenseData 加密保护）
     *   6. 调用 service.encrypt(...) -> 得到 LicenseFile（三份内容：.dat / .lic / .sig）
     *   7. 指定格式（TEXT）便于后续校验或打印
     *   8. 调用 io.writeZip(...) -> 打包生成 *.zip（包含三份文件）
     *   9. 将最终 zip 写入存储（本地文件系统）
     * </pre>
     */
    @Test
    @Order(1)
    public void writeEncrypt() {
        // 📝 Step 1: 记录日志，准备写入新的 License 文件
        log.info("[ R2MOMO ] 写入新的 License！");

        // 📂 Step 2: 从资源目录加载 JSON 数据，反序列化为 LicenseData 对象
        //     - JSON 文件路径: HED/source/license_2.json
        //     - 类型: LicenseData.class
        final LicenseData licenseData = this.inOne("HED/source/license_2.json", LicenseData.class);

        // 🔑 Step 3: 追加硬件指纹（signFinger）
        //     - 使用 HED.fingerHex() 生成 SHA-256 指纹（64 hex）
        //     - 绑定许可与具体机器环境
        licenseData.setSignFinger(HED.fingerHex());

        // 🔎 Step 4: 打印调试日志，查看加载后的 LicenseData 数据内容
        log.info("[ R2MOMO ] 读取 License 数据：{}", licenseData);

        // 📦 Step 5: 构造 LicenseConfiguration
        //     - 控制许可文件存储位置
        //     - 包含公私钥目录
        //     - 参数 true -> 指定是否覆盖或强制生成
        final LicenseConfiguration licenseConfiguration = this.createConfiguration(true);

        // 📌 Step 6: 打印许可配置路径信息，确认存储目录正确性
        log.info("[ R2MOMO ] 许可证位置：\n{}", licenseConfiguration);

        // 🔒 Step 7: 从存储中加载私钥
        //     - 用于对 LicenseData 签名
        //     - 路径来源: licenseConfiguration.ioPrivate()
        final PrivateKey privateKey = store.inPrivate(licenseConfiguration.ioPrivate());

        // 🔐 Step 8: 生成对称密钥 SecretKey
        //     - 使用算法 ENC.value()（例如 AES-256）
        //     - 用于对许可数据加密
        final SecretKey secretKey = HED.genSecretKey(ENC.value());

        // ⚙️ Step 9: 使用服务层加密生成 LicenseFile
        //     - 输入：LicenseData + 私钥 + 对称密钥
        //     - 输出：LicenseFile（包含 data/加密内容/签名）
        //       - data()      -> *.dat 原始许可 JSON
        //       - encrypted() -> *.lic 加密许可文件
        //       - signature() -> *.sig 数字签名
        final LicenseFile licenseFile = service.encrypt(licenseData, privateKey, secretKey);

        // 📝 Step 10: 指定输出格式为 TEXT（方便后续调试或人工校验）
        licenseFile.format(LicFormat.TEXT);

        // 📦 Step 11: 写入存储
        //     - io.writeZip(...) 会打包 *.dat, *.lic, *.sig -> *.zip
        //     - 返回值 Binary -> 内存中 zip 压缩流
        final Binary in = io.writeZip(licenseFile, licenseConfiguration);

        // 📏 Step 12: 打印最终生成文件的字节长度，确认文件大小合理
        log.info("[ R2MOMO ] 文件尺寸：{}", in.length());

        // 💾 Step 13: 将生成的 zip 文件写入本地存储
        //     - 输出路径: HED/licensing/generated/license_2.zip
        //     - 便于后续分发或上传给客户
        store.write("HED/licensing/generated/license_2.zip", in);
    }

    /**
     * 测试：读取并验证已有的 License 文件（加密版本）
     * <pre>
     * 数据流：
     *   1. 加载原始 JSON -> 转换成 LicenseData（业务实体数据）
     *   2. 动态追加机器指纹（signFinger），确保与当前硬件绑定
     *   3. 构造 LicenseConfiguration（encrypted=true，表示需要加载密钥文件解密）
     *   4. 构造 LicensePath，用于定位 *.lic / *.sig / *.key
     *   5. 从存储中读取 License 内容（加密态字符串）
     *   6. 调用 io.readIn(content, formatFail, configuration) -> 反序列化为 LicenseFile（只包含 data/encrypted）
     *   7. 再次调用 io.readIn(licensePath, configuration) -> 加载签名文件、密钥文件等安全信息
     *   8. 调用 licenseFile.loadMetadata(...) -> 将安全元信息合并到完整的 LicenseFile
     *   9. 打印 LicenseFile，确认解密及元信息是否正确加载
     *  10. 调用 io.verify(...) -> 解密 encrypted 内容，并基于公钥验签
     *  11. 返回最终 LicenseData（业务实体数据，已确认合法性与完整性）
     * </pre>
     */
    @Test
    @Order(2)
    public void readLic() {
        log.info("[ R2MOMO ] 读取已有 License！");

        // 📂 Step 1: 从资源目录加载 JSON 数据
        final LicenseData licenseData = this.inOne("HED/source/license_2.json", LicenseData.class);

        // 🔑 Step 2: 设置硬件指纹，确保与写入时一致
        licenseData.setSignFinger(HED.fingerHex());

        // 📦 Step 3: 构造配置
        //     - true 表示加密模式，需要额外加载密钥文件 (*.key) 来解密
        final LicenseConfiguration licenseConfiguration = this.createConfiguration(true);

        // 📌 Step 4: 构造路径对象（包含 .lic/.sig/.key 文件路径）
        final LicensePath licensePath = this.createPath(licenseData, FORMAT);

        // 📖 Step 5: 从存储读取 License 内容（这里拿到的是加密态）
        final String content = this.readLic(licenseData, FORMAT, licenseConfiguration);
        log.info("[ R2MOMO ] 许可内容：\n{}", content);

        // 🗂 Step 6: 将内容解析为 LicenseFile（初步，仅含 data/encrypted）
        final LicenseFile licenseFile = io.readIn(content, FORMAT, licenseConfiguration);

        // 🗂 Step 7: 从路径再次加载 LicenseFile（包含签名与密钥）
        final LicenseFile securityFile = io.readIn(licensePath, licenseConfiguration);

        // 🔄 Step 8: 合并元信息（将签名和密钥加载到 LicenseFile 中）
        licenseFile.loadMetadata(securityFile);

        // 🔎 Step 9: 打印 LicenseFile，确认是否包含完整信息
        log.info("[ R2MOMO ] 读取 License 文件：{}", licenseFile);

        // ✅ Step 10: 验证 License
        //     - 解密 encrypted 字段
        //     - 公钥验签（确认数据未被篡改）
        final LicenseData data = io.verify(licenseFile, licenseConfiguration);

        // 📌 Step 11: 打印最终业务数据
        log.info("[ R2MOMO ] 验证 License 数据：{}", data);
    }
}
